#include "te_GameEngine.h"

GameEngine *GameEngine::instance = NULL;
bool GameEngine::killedGame = false;
int GameEngine::startArgc = 0;
char **GameEngine::startArgv = NULL;

using namespace og;

bool CVar::linkedToEngine = false;
CVar *CVar::firstLink = NULL;

CVar fs_game("fs_game", "", CVAR_INIT, "Current mod that the game is running");
CVar fs_basepath("fs_basepath", ".", CVAR_INIT, "The directory holding all of the game data");
CVar fs_homepath("fs_homepath", ".", CVAR_INIT, "Directory used for all write access");	// changeme on *nix

// Basic class handling (nothing special):
GameEngine::GameEngine()
{
	video_restart = false;

	CON_Init();

	FS_Init();

	ConfigRead();

	AddEngineCommands();

	VM_Create();

	exports->GameInit();
}

GameEngine::~GameEngine()
{
	exports->GameShutdown();

	VM_Destroy();

	R_Shutdown();

	cvarSystemEx->WriteToConfig(GAME_CONFIG);

	FS_Shutdown();

	CON_Shutdown();
}

GameEngine *GameEngine::GetSingleton()
{
	if(GameEngine::instance == NULL)
	{
		GameEngine::instance = new GameEngine();
	}
	return GameEngine::instance;
}

void GameEngine::DestroySingleton()
{
	delete GameEngine::instance;
}

void GameEngine::KillGameInstance()
{
	GameEngine::killedGame = true;
}

// Game Loop
void GameEngine::Run()
{
	while(!killedGame)
	{
		Renderer *Render = Renderer::GetSingleton();
		while(!video_restart && Render->Run() && !killedGame)
		{
			exports->RunFrame();
			Console::Synchronize();
		}

		if(!video_restart)
		{
			break;	// die fgt
		}
		video_restart = false;
		Render->irrDevice->closeDevice();
		Render->Run();
		Render->irrDevice->drop();
		Render->Init();
	}
}

void GameEngine::CON_Init()
{
	ConsoleParams_t conParms;
	conParms.appTitle = GAME_NAME;

#if OG_WIN32
	conParms.width = 400;
	conParms.height = 250;
	conParms.minWidth = 400;
	conParms.minHeight = 250;
	conParms.fadeTime = 500;

	conParms.logBgImage = NULL;
#define SETCOLORPARM(n,r,g,b) conParms.n[0]=r;conParms.n[1]=g;conParms.n[2]=b;
	SETCOLORPARM( colorLogBg, 0x50, 0x50, 0x50 );
	SETCOLORPARM( colorLogText, 0xff, 0xff, 0xff );
	SETCOLORPARM( colorErrorBg, 0x80, 0x80, 0x80 );
	SETCOLORPARM( colorErrorBgFlash, 0xff, 0x00, 0x00 );
	SETCOLORPARM( colorErrorText, 0xff, 0xff, 0xff );
	SETCOLORPARM( colorErrorTextFlash, 0xff, 0xff, 0xff );
#undef SETCOLORPARM
#endif

	//og::Console::ShowConsole((og::Console::VisLevel)0, false);
	Console::Init( &conParms );
	Console::Print("Console started\n");

	// Do the startup args
	bool inCommand = false;
	std::string currentArg;
	for(int i = 1; i < GameEngine::startArgc; i++)	// always skip the first arg, since that's the command used to start the program
	{
		// Concatenate the args thusly:
		// Everything with a '+' in front of it gets treated like its own arg.
		// So for instance:
		// +set fs_game "potato" +set r_renderer opengl
		// This has two args:
		// "set fs_game \"potato\"
		// "set r_renderer opengl
		// The game executes the command immediately once all of the true args have been dealt with
		if(inCommand && GameEngine::startArgv[i] && GameEngine::startArgv[i][0] == '+')
		{
			// Command is finished. Take what we have and execute it.
			cmdSystemEx->ExecuteCmd(currentArg.c_str()+1, true);
			currentArg.clear();
			currentArg = GameEngine::startArgv[i];
			currentArg += " ";
		}
		else if(inCommand)
		{
			currentArg += GameEngine::startArgv[i];
			currentArg += " ";
		}
		else if(!inCommand && GameEngine::startArgv[i] && GameEngine::startArgv[i][0] == '+')
		{
			// Start dealing with this as a command
			currentArg = GameEngine::startArgv[i];
			currentArg += " ";
			inCommand = true;
		}
		else if(!inCommand)
		{
			// Just execute it I guess
			cmdSystemEx->ExecuteCmd(GameEngine::startArgv[i], true);
		}
	}
	if(inCommand && currentArg.length() > 0)
	{
		// Last arg, so it gets executed immediately
		cmdSystemEx->ExecuteCmd(currentArg.c_str()+1, true);
	}
}

void GameEngine::CON_Shutdown()
{
	Console::Shutdown();
}

void GameEngine::R_Shutdown()
{
	Renderer::DestroySingleton();
}

void GameEngine::FS_Init()
{
	int fs_numFiles = 0;
	//FileSystem::SimpleInit(PAK_EXTENSION, BASE_DIR, SEARCH_DIR, SAVE_DIR);
	// Out with the old method, in with the new
	Console::Print("-- GameEngine::FS_Init() -- \n");

	// TODO: *nix support

	FileSystem::Prepare();

	if( fs_basepath.GetString()[0] )
	{
		FileSystem::AddSearchPath( fs_basepath.GetString() );
	}

	// TODO: fs_apppath (Mac)

	if( fs_homepath.GetString()[0] && strcmp(fs_homepath.GetString(), fs_basepath.GetString()) )
	{
		FileSystem::MakePath( fs_homepath.GetString() );
		FileSystem::SetSavePath( fs_homepath.GetString() );
	}
	else if( fs_basepath.GetString()[0] )
		FileSystem::SetSavePath( fs_basepath.GetString() );

	if(!FileSystem::Init(PAK_EXTENSION, BASE_DIR))
	{
		Console::FatalError("Couldn't initialize filesystem!");
		return;
	}

	// Get the additional stuff in fs_game
	if( fs_game.GetString()[0] && strcmp(fs_game.GetString(), BASE_DIR) )
	{
		if(!FileSystem::ChangeMod( fs_game.GetString(), BASE_DIR ))
		{
			Console::FatalError("Couldn't change current game...closing");
			return;
		}
	}

	if( !FS->FileExists( "default.cfg" ) )
	{
		Console::FatalError("Couldn't load default.cfg");
		return;
	}

	Console::Print( "----------------------\n" );
}

void GameEngine::FS_Shutdown()
{
	FileSystem::Shutdown();
}

void GameEngine::ConfigRead()
{
	if(FS->FileExists(GAME_CONFIG))
	{
		cmdSystemEx->ExecuteConfig(GAME_CONFIG);
	}
	else
	{
		Console::Print("config not found, writing a new one at game end\n");
	}
}

void GameEngine::Video_Restart()
{
	video_restart = true;
}